Maximum Sharpe Ratio Portfolio

Maximum Sharpe Ratio Portfolio#

import numpy as np
from plotly.io import show
from sklearn.model_selection import train_test_split

from skfolio import Population, RiskMeasure
from skfolio.datasets import load_sp500_dataset
from skfolio.optimization import InverseVolatility, MeanRisk, ObjectiveFunction
from skfolio.preprocessing import prices_to_returns

Data Preparation#

price = load_sp500_dataset()
price.head()
AAPL AMD BAC BBY CVX GE HD JNJ JPM KO LLY MRK MSFT PEP PFE PG RRC UNH WMT XOM
Date
1990-01-02 0.264 4.125 4.599 0.144 4.991 14.391 1.117 3.438 3.394 2.235 6.658 4.215 0.384 4.738 1.021 3.860 3.322 0.310 3.653 4.068
1990-01-03 0.266 4.000 4.636 0.161 4.910 14.364 1.121 3.452 3.508 2.203 6.658 4.282 0.386 4.692 1.024 3.853 3.322 0.304 3.653 4.027
1990-01-04 0.267 3.938 4.537 0.159 4.847 14.283 1.128 3.459 3.522 2.192 6.621 4.215 0.397 4.646 1.041 3.777 3.322 0.301 3.634 3.987
1990-01-05 0.268 3.812 4.438 0.159 4.775 14.148 1.113 3.423 3.536 2.174 6.549 4.128 0.387 4.581 1.032 3.709 3.322 0.288 3.595 3.966
1990-01-08 0.269 3.812 4.463 0.147 4.820 14.229 1.102 3.481 3.536 2.220 6.549 4.181 0.393 4.664 1.023 3.777 3.322 0.282 3.644 4.027
asset_linear_returns = prices_to_returns(price)
asset_linear_returns.head()
AAPL AMD BAC BBY CVX GE HD JNJ JPM KO LLY MRK MSFT PEP PFE PG RRC UNH WMT XOM
Date
1990-01-03 0.007576 -0.030303 0.008045 0.118056 -0.016229 -0.001876 0.003581 0.004072 0.033589 -0.014318 0.000000 0.015896 0.005208 -0.009709 0.002938 -0.001813 0.0 -0.019355 0.000000 -0.010079
1990-01-04 0.003759 -0.015500 -0.021355 -0.012422 -0.012831 -0.005639 0.006244 0.002028 0.003991 -0.004993 -0.005557 -0.015647 0.028497 -0.009804 0.016602 -0.019725 0.0 -0.009868 -0.005201 -0.009933
1990-01-05 0.003745 -0.031996 -0.021821 0.000000 -0.014855 -0.009452 -0.013298 -0.010408 0.003975 -0.008212 -0.010874 -0.020641 -0.025189 -0.013991 -0.008646 -0.018004 0.0 -0.043189 -0.010732 -0.005267
1990-01-08 0.003731 0.000000 0.005633 -0.075472 0.009424 0.005725 -0.009883 0.016944 0.000000 0.021159 0.000000 0.012839 0.015504 0.018118 -0.008721 0.018334 0.0 -0.020833 0.013630 0.015381
1990-01-09 -0.007435 0.016527 0.000000 0.000000 -0.007469 -0.020803 -0.026316 -0.031026 -0.031957 -0.007658 -0.011147 -0.007893 -0.002545 -0.013722 -0.021505 0.000000 0.0 -0.024823 -0.026619 -0.020114
X_train, X_test = train_test_split(asset_linear_returns, shuffle=False)
X_test.head()
AAPL AMD BAC BBY CVX GE HD JNJ JPM KO LLY MRK MSFT PEP PFE PG RRC UNH WMT XOM
Date
2014-09-29 -0.006353 -0.013889 -0.001183 0.021517 -0.007574 -0.008196 0.000421 -0.005226 -0.003803 0.001190 -0.002136 0.000347 0.000649 0.000207 0.001677 -0.001653 0.014331 -0.001042 -0.005356 -0.010485
2014-09-30 0.006394 -0.039437 0.002369 -0.001782 -0.010200 0.007874 -0.012268 0.000464 -0.001493 0.009698 -0.007512 -0.002199 -0.001721 -0.000635 -0.006698 -0.008292 -0.012518 -0.002997 0.005132 -0.004013
2014-10-01 -0.015594 -0.017595 -0.013486 -0.028875 -0.013996 -0.017960 -0.007849 -0.021480 -0.007796 0.001859 0.003552 -0.009603 -0.009920 -0.004724 -0.014230 -0.007156 -0.005323 -0.015893 -0.004585 -0.012653
2014-10-02 0.007266 0.011940 0.003594 0.003063 -0.004596 -0.001590 0.013405 -0.004310 -0.008934 -0.001856 -0.003836 0.002389 -0.003054 -0.000763 -0.003068 -0.001090 0.007563 0.000242 0.001441 0.004734
2014-10-03 -0.002778 0.002950 0.024294 0.019871 0.005126 0.011145 0.014090 0.012316 0.024827 0.007963 0.012947 0.017827 0.007215 0.009944 0.005499 0.008914 -0.017500 0.013185 0.014306 0.006647

Model#

Create a Maximize Sharpe Ratio model and fit it on training set.

max_sharpe_model = MeanRisk(
    risk_measure=RiskMeasure.STANDARD_DEVIATION,
    objective_function=ObjectiveFunction.MAXIMIZE_RATIO,
    portfolio_params=dict(name="Max Sharpe Ratio"),
)
max_sharpe_model.fit(X_train)
max_sharpe_model.weights_
array([9.12639732e-02, 2.85049328e-08, 2.31977012e-08, 1.04246239e-01,
       3.08020521e-02, 3.60047992e-08, 4.18252122e-02, 1.72210429e-01,
       4.01080513e-08, 7.82855158e-03, 8.14858866e-08, 8.52061207e-08,
       1.12788995e-01, 7.13632483e-02, 2.64892622e-07, 1.10516792e-01,
       7.81272384e-02, 1.75517534e-01, 2.39852902e-07, 3.50893616e-03])
benchmark_model = InverseVolatility(
    portfolio_params=dict(name="Inverse Volatility")
)
benchmark_model.fit(X_train)
benchmark_model.weights_
array([0.03309451, 0.02507122, 0.03536272, 0.02895846, 0.06352572,
       0.05434978, 0.04758711, 0.07073444, 0.03891936, 0.06692204,
       0.05564459, 0.05576718, 0.04704164, 0.0638535 , 0.05597692,
       0.06760446, 0.02585011, 0.03990667, 0.0577429 , 0.06608667])

Prediction#

The predict method returns a portfolio object.

pred_max_sharpe = max_sharpe_model.predict(X_test)
pred_benchmark = benchmark_model.predict(X_test)
pred_max_sharpe
<Portfolio Max Sharpe Ratio>
pred_benchmark
<Portfolio Inverse Volatility>

Print some properties.

print(pred_max_sharpe.returns)
print(pred_benchmark.returns)
print(pred_max_sharpe.annualized_sharpe_ratio)
print(pred_benchmark.annualized_sharpe_ratio)
[ 0.00136218 -0.00294776 -0.01437361 ...  0.0082964   0.00104224
 -0.01662141]
[-0.00206867 -0.00293476 -0.01046662 ...  0.0086359   0.00202183
 -0.0113165 ]
0.9684677102652403
0.8930043602505584
pred_max_sharpe.plot_cumulative_returns()
pred_benchmark.plot_cumulative_returns()
pred_max_sharpe.summary()
Mean                                             0.072%
Annualized Mean                                  18.14%
Variance                                         0.014%
Annualized Variance                               3.51%
Semi-Variance                                   0.0069%
Annualized Semi-Variance                          1.75%
Standard Deviation                                1.18%
Annualized Standard Deviation                    18.73%
Semi-Deviation                                    0.83%
Annualized Semi-Deviation                        13.22%
Mean Absolute Deviation                           0.76%
CVaR at 95%                                       2.70%
EVaR at 95%                                       5.42%
Worst Realization                                10.41%
CDaR at 95%                                      14.09%
MAX Drawdown                                     31.85%
Average Drawdown                                  2.98%
EDaR at 95%                                      19.42%
First Lower Partial Moment                        0.38%
Ulcer Index                                       0.048
Gini Mean Difference                              1.15%
Value at Risk at 95%                              1.66%
Drawdown at Risk at 95%                          10.33%
Entropic Risk Measure at 95%                       3.00
Fourth Central Moment                         0.000038%
Fourth Lower Partial Moment                   0.000014%
Skew                                             35.24%
Kurtosis                                       1942.93%
Sharpe Ratio                                      0.061
Annualized Sharpe Ratio                            0.97
Sortino Ratio                                     0.086
Annualized Sortino Ratio                           1.37
Mean Absolute Deviation Ratio                     0.095
First Lower Partial Moment Ratio                   0.19
Value at Risk Ratio at 95%                        0.043
CVaR Ratio at 95%                                 0.027
Entropic Risk Measure Ratio at 95%              0.00024
EVaR Ratio at 95%                                 0.013
Worst Realization Ratio                          0.0069
Drawdown at Risk Ratio at 95%                    0.0070
CDaR Ratio at 95%                                0.0051
Calmar Ratio                                     0.0023
Average Drawdown Ratio                            0.024
EDaR Ratio at 95%                                0.0037
Ulcer Index Ratio                                 0.015
Gini Mean Difference Ratio                        0.063
Effective Number of Assets            8.434345967826763
Assets Number                                        20
dtype: object

Analysis#

For holistic analysis, load both portfolio into a Population class object.

population = Population([pred_max_sharpe, pred_benchmark])
population.plot_composition()
population.plot_cumulative_returns()

Full evaluation summary.

population.summary()
Max Sharpe Ratio Inverse Volatility
Mean 0.072% 0.062%
Annualized Mean 18.14% 15.52%
Variance 0.014% 0.012%
Annualized Variance 3.51% 3.02%
Semi-Variance 0.0069% 0.0062%
Annualized Semi-Variance 1.75% 1.57%
Standard Deviation 1.18% 1.09%
Annualized Standard Deviation 18.73% 17.38%
Semi-Deviation 0.83% 0.79%
Annualized Semi-Deviation 13.22% 12.52%
Mean Absolute Deviation 0.76% 0.69%
CVaR at 95% 2.70% 2.57%
EVaR at 95% 5.42% 5.51%
Worst Realization 10.41% 10.49%
CDaR at 95% 14.09% 13.21%
MAX Drawdown 31.85% 34.81%
Average Drawdown 2.98% 2.59%
EDaR at 95% 19.42% 20.91%
First Lower Partial Moment 0.38% 0.35%
Ulcer Index 0.048 0.044
Gini Mean Difference 1.15% 1.06%
Value at Risk at 95% 1.66% 1.55%
Drawdown at Risk at 95% 10.33% 9.47%
Entropic Risk Measure at 95% 3.00 3.00
Fourth Central Moment 0.000038% 0.000029%
Fourth Lower Partial Moment 0.000014% 0.000015%
Skew 35.24% -13.45%
Kurtosis 1942.93% 1988.50%
Sharpe Ratio 0.061 0.056
Annualized Sharpe Ratio 0.97 0.89
Sortino Ratio 0.086 0.078
Annualized Sortino Ratio 1.37 1.24
Mean Absolute Deviation Ratio 0.095 0.089
First Lower Partial Moment Ratio 0.19 0.18
Value at Risk Ratio at 95% 0.043 0.040
CVaR Ratio at 95% 0.027 0.024
Entropic Risk Measure Ratio at 95% 0.00024 0.00021
EVaR Ratio at 95% 0.013 0.011
Worst Realization Ratio 0.0069 0.0059
Drawdown at Risk Ratio at 95% 0.0070 0.0065
CDaR Ratio at 95% 0.0051 0.0047
Calmar Ratio 0.0023 0.0018
Average Drawdown Ratio 0.024 0.024
EDaR Ratio at 95% 0.0037 0.0029
Ulcer Index Ratio 0.015 0.014
Gini Mean Difference Ratio 0.063 0.058
Effective Number of Assets 8.434345967826763 18.441257685468493
Assets Number 20 20